package com.funny.stock.data.collect.impl;

import com.funny.stock.base.util.DateUtil;
import com.funny.stock.base.util.PropertiesCopyUtil;
import com.funny.stock.data.collect.HistoryDataService;
import com.funny.stock.data.collect.source.JuHeStockDataSource;
import com.funny.stock.data.collect.source.NetsStockDataSource;
import com.funny.stock.data.dao.entity.StockDO;
import com.funny.stock.data.dao.entity.StockDayDataDO;
import com.funny.stock.data.dao.entity.StockDayDataResult1DO;
import com.funny.stock.data.dao.repo.StockDayDataRepository;
import com.funny.stock.data.dao.repo.StockDayDataResult1Repository;
import com.funny.stock.data.dao.repo.StockRepository;
import com.funny.stock.data.vo.Stock;
import com.funny.stock.data.vo.StockDayData;
import com.funny.stock.data.vo.StockType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author pengl
 * @date 2020/05/03
 **/
@Service
public class HistoryDataServiceImpl implements HistoryDataService {
    public static final Logger logger = LoggerFactory.getLogger(HistoryDataServiceImpl.class);

    private Lock lock = new ReentrantLock();

    @Autowired
    NetsStockDataSource dataSource;

    @Autowired
    JuHeStockDataSource juHeDataSource;

    @Autowired
    StockDayDataRepository repository;

    @Autowired
    StockDayDataResult1Repository dayDataResult1Repository;

//    @Autowired
//    StockDataService stockDataService;

    @Autowired
    TaskExecutor taskExecutor;

    @Autowired
    StockRepository stockRepository;

    @Override
    public List<StockDayData> getDayData(String code, int year) {
        String startYear = String.valueOf(year);
        Date startDate = DateUtil.string2Date(startYear, "yyyy");
//        String dateStr = DateUtil.da(date);
//        String yearStr = date.getYear() + "";

        List<StockDayDataDO> queryList = repository.queryAllByDateLike(code, startYear);

//        if(CollectionUtils.isEmpty(queryList)) {
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    loadDataAsync(code, startDate);
                }
            });
//        }

        return queryList.stream()
                .map(item -> PropertiesCopyUtil.copyPropertiesIgnore(item,StockDayData.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<StockDayData> getDataByRange(String code, Date startDate, Date endDate) {
        String startDateStr = DateUtil.date2dateString(startDate);
        String endDateStr = DateUtil.date2dateString(endDate);

        List<StockDayDataDO> queryList = repository.queryAllByDateRange(code, startDateStr, endDateStr);

        return queryList.stream()
                .map(item -> PropertiesCopyUtil.copyPropertiesIgnore(item,StockDayData.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public List<StockDayDataResult1DO> calcDataByRange(String code, Date startDate, Date endDate) {
        String startDateStr = DateUtil.date2dateString(startDate);
        String endDateStr = DateUtil.date2dateString(endDate);

        List<StockDayDataDO> queryList = repository.queryAllByDateRange(code, startDateStr, endDateStr);
        if(queryList.size() != 0)
            dayDataResult1Repository.deleteByDateAfterAndDateBefore(startDate,endDate);



        List<StockDayDataResult1DO> collect = queryList.stream()
                .map(item -> {
                    StockDayDataResult1DO stockDayDataResult1DO = PropertiesCopyUtil.copyPropertiesIgnore(item, StockDayDataResult1DO.class);
                    stockDayDataResult1DO.setId(null);

                    return stockDayDataResult1DO;
                })
                .collect(Collectors.toList());

        calStockDayDataResult(collect,queryList);

        List<StockDayDataResult1DO> stockDayDataResult1DOS = dayDataResult1Repository.saveAll(collect);
        return stockDayDataResult1DOS;
    }


    private BigDecimal calcPercent(BigDecimal start, BigDecimal end){
        if(start.equals(BigDecimal.ZERO)) return BigDecimal.ZERO;
        return end.subtract(start)
                .multiply(BigDecimal.valueOf(100))
                .divide(start, 2, BigDecimal.ROUND_HALF_UP);
    }


    public void calStockDayDataResult(List<StockDayDataResult1DO> list, List<StockDayDataDO> allDataList){
        list.sort(Comparator.comparing(StockDayDataResult1DO::getDate));
        allDataList.sort(Comparator.comparing(StockDayDataDO::getDate));

        Map<String,Integer> dayDataMap = new HashMap<>();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < allDataList.size(); i++) {
            StockDayDataDO item = allDataList.get(i);
            dayDataMap.put(dateFormat.format(item.getDate()), i);
        }

        for (StockDayDataResult1DO item : list) {
            //前一天收盘价  当日开盘价  开盘时涨幅  当日最高价  最高涨幅  当日最低价  最低涨幅  收盘价 收盘时涨幅 当日振幅
            LocalDate localDate = item.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            Integer curDayIndex = dayDataMap.get(localDate.toString());

            item.setMaxIncreasePer(calcPercent(item.getOpenPrice(),item.getMaxPrice()));
            item.setLowIncreasePer(calcPercent(item.getOpenPrice(),item.getLowPrice()));

            BigDecimal beforeDayClosePrice = BigDecimal.ZERO;
            if(curDayIndex != null && curDayIndex > 0) {
                beforeDayClosePrice = allDataList.get(curDayIndex - 1).getClosePrice();
                item.setYesterdayClosePrice(beforeDayClosePrice);

                item.setOpenIncreasePer(calcPercent(beforeDayClosePrice,item.getOpenPrice()));

                if(item.getOpenIncreasePer().compareTo(BigDecimal.ZERO) < 0
                        && item.getOpenIncreasePer().compareTo(item.getLowIncreasePer()) < 0){
                    //除权情况
                    item.setOpenIncreasePer(BigDecimal.ZERO);
                }
            }else {
                item.setYesterdayClosePrice(BigDecimal.ZERO);
                item.setOpenIncreasePer(BigDecimal.ZERO);
            }



        }

    }

    @Override
    public StockDayData getDayBeforeData(Date startDate) {
        return null;
    }

    @Override
    public void loadStockDataList(StockType type) {
        taskExecutor.execute(() -> {
            if(lock.tryLock()) {
                try {
                    doLoadStockDataList(type);
                }finally {
                    lock.unlock();
                }
            }
        });
    }

    private void doLoadStockDataList(StockType type){
        if(type == null) type = StockType.SH;
//
        int page = 1;
        int pageSize = 80;
        List<Stock> onlineStockList;

        do{
            onlineStockList = juHeDataSource.getOnlineStockList(page, pageSize,type);
            List<StockDO> stockDOList = PropertiesCopyUtil.copyPropertiesByList(onlineStockList, StockDO.class);
            List<String> codeList = stockDOList.stream().map(item -> item.getCode()).collect(Collectors.toList());
            List<StockDO> queryStockDOList = stockRepository.findByCodeIn(codeList);

            Map<String, StockDO> stockDOMap = queryStockDOList.stream().collect(Collectors.toMap(StockDO::getCode, Function.identity(), (k1, k2) -> k2));

            for (StockDO stockDO : stockDOList) {
                StockDO existsDO = stockDOMap.get(stockDO.getCode());
                if(existsDO != null){
                    stockDO.setId(existsDO.getId());
                }
            }


            stockRepository.saveAll(stockDOList);
            logger.info("保存股票基础数据成功:{}",stockDOList.size());
            page++;
        }while (onlineStockList != null && onlineStockList.size() != 0);
    }

    private void loadDataAsync(String code, Date date) {
        StockDO stockDO = stockRepository.findByCode(code);
        StockType type = StockType.getValueByType(stockDO.getMarket());
        dataSource.getStockDataByDate(code, type, date, new Date());
    }

}
